const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`;
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
// 匹配到的分组是一个标签名 <xxx 匹配到的是开始标签的名字
const startTagOpen = new RegExp(`^<${qnameCapture}`);
// 开始标签的结束
const startTagClose = /^\s*(\/?)>/;
// 匹配到的</xxx> 匹配到的分组是结束标签的名字
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`);
// 匹配属性  第一个分组就是属性的key value就是分组3/分组4/分组5
const attribute =
  /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;

// 解析html
// > 将template转化成ast语法树
export function parseHTML(html) {
  const ELEMENT_TYPE = 1;
  const TEXT_TYPE = 3;
  const stack = []; // 用于存放元素的栈
  let currentParent; // 指向的是栈中最后一个
  let root;

  // 最终需要转化成一颗抽象语法树
  function createASTElement(tag, attrs) {
    return {
      tag,
      type: ELEMENT_TYPE,
      children: [],
      attrs,
      parent: null,
    };
  }

  /* 利用栈形结构来构造树 */
  function start(tag, attrs) {
    let node = createASTElement(tag, attrs); // 创造一个ast节点
    if (!root) {
      // 看一下是否为空树
      root = node;
    }
    if (currentParent) {
      node.parent = currentParent; // 赋值node的parent属性
      //将node赋值给当前标签的children
      currentParent.children.push(node);
    }
    stack.push(node);
    currentParent = node; //currentParent指向的是栈中最后一个
  }
  function charts(text) {
    text = text.replace(/\s/g, "");
    // 文本直接放到当前指向的节点中
    text &&
      currentParent.children.push({
        type: TEXT_TYPE,
        text,
        parent: currentParent,
      });
  }
  function end(tag) {
    stack.pop(); // 弹出最后一个
    // 比较弹出的标签和tag可以校验标签是否合法
    currentParent = stack[stack.length - 1];
  }

  /* 截取 */
  function advance(n) {
    html = html.substring(n);
  }

  /* 解析开始标签 */
  function parseStartTag() {
    const start = html.match(startTagOpen);
    if (start) {
      const match = {
        tagName: start[1], // 标签名
        attrs: [],
      };
      advance(start[0].length);

      // 如果不是开始标签的结束，就一直匹配
      let attr, end;
      while (
        !(end = html.match(startTagClose)) &&
        (attr = html.match(attribute))
      ) {
        // 匹配到属性，赋值给attr，并将其截掉
        match.attrs.push({
          name: attr[1],
          value: attr[3] || attr[4] || attr[5] || true,
        });
        advance(attr[0].length);
      }

      // 如果匹配到结束标识，将其删掉
      if (end) {
        advance(end[0].length);
      }

      // console.log(match);
      return match;
    }

    return false; // 不是开始标签
  }

  while (html) {
    // <div>牛牛</div>
    // 如果textEnd为0,说明是一个开始标签或者结束标签
    // 如果textEnd>0,说明就是文本的结束位置
    let textEnd = html.indexOf("<");

    // 解析到 <
    if (textEnd == 0) {
      // 解析开始标签
      const startTagMatch = parseStartTag();
      if (startTagMatch) {
        start(startTagMatch.tagName, startTagMatch.attrs);
        continue;
      }

      // 解析结束标签
      let endTagMatch = html.match(endTag);
      if (endTagMatch) {
        advance(endTagMatch[0].length);
        end(endTagMatch[1]);
        continue;
      }
    }

    // 解析文本
    if (textEnd > 0) {
      let text = html.substring(0, textEnd); // 文本内容
      if (text) {
        advance(text.length);
        charts(text);
      }
    }
  }

  return root;
}
